package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sync/atomic"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"golang.org/x/sys/windows/svc"
	"golang.org/x/sys/windows/svc/eventlog"
	"golang.org/x/sys/windows/svc/mgr"

	"gopkg.in/natefinch/lumberjack.v2"

	"net/http"

	"github.com/gin-gonic/gin"
)

const serviceName = "AudioPlayService"

var (
	zapLogger *zap.SugaredLogger

	// 全局变量用于统计
	startTime       = time.Now()
	networkRequests uint64
)

func main() {
	initZapLogger()
	defer zapLogger.Sync()

	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "install":
			exePath, _ := os.Executable()
			if err := installService(serviceName, exePath); err != nil {
				zapLogger.Fatalf("安装服务失败: %v", err)
			}
			zapLogger.Info("服务安装成功")
			return
		case "uninstall":
			if err := uninstallService(serviceName); err != nil {
				zapLogger.Fatalf("卸载服务失败: %v", err)
			}
			zapLogger.Info("服务卸载成功")
			return
		}
	}

	isService, err := svc.IsWindowsService()
	if err != nil {
		zapLogger.Fatalf("判断是否服务失败: %v", err)
	}

	if isService {
		zapLogger.Info("以服务方式运行")
		runService(serviceName)
	} else {
		zapLogger.Info("以控制台方式运行")
		go startGinServer() // 确保 Gin 在控制台模式下启动
		select {
		case <-time.After(1 * time.Hour): // 设置超时，避免死锁
			zapLogger.Info("服务运行超时，退出")
			return
		}
	}
}

// 注册服务并配置自动重启
func installService(name, exePath string) error {
	m, err := mgr.Connect()
	if err != nil {
		return err
	}
	defer m.Disconnect()

	s, err := m.OpenService(name)
	if err == nil {
		s.Close()
		log.Println("服务已存在，无需安装")
		return nil
	}

	s, err = m.CreateService(name, exePath, mgr.Config{
		DisplayName: name,
		StartType:   mgr.StartAutomatic,
		Description: "一个支持自动重启和事件日志的Go服务示例",
	})
	if err != nil {
		return err
	}
	defer s.Close()

	// 注册事件日志
	if err := eventlog.InstallAsEventCreate(name, eventlog.Info|eventlog.Warning|eventlog.Error); err != nil {
		s.Delete()
		return err
	}

	// 配置自动重启
	err = exec.Command("sc", "failure", name,
		"reset=86400",
		"actions=restart/5000/restart/5000/restart/5000").Run()
	if err != nil {
		log.Printf("设置自动重启失败: %v", err)
	} else {
		log.Println("已启用失败后自动重启")
	}

	return nil
}

func uninstallService(name string) error {
	m, err := mgr.Connect()
	if err != nil {
		return err
	}
	defer m.Disconnect()

	s, err := m.OpenService(name)
	if err != nil {
		return err
	}
	defer s.Close()

	_ = eventlog.Remove(name)
	return s.Delete()
}

func runService(name string) {
	err := svc.Run(name, &myService{name: name})
	if err != nil {
		log.Fatalf("服务运行失败: %v", err)
	}
}

type myService struct {
	name string
}

func (m *myService) Execute(args []string, req <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {
	elog, err := eventlog.Open(m.name)
	if err != nil {
		return false, 1
	}
	defer elog.Close()

	status <- svc.Status{State: svc.StartPending}
	logBoth(elog, "服务正在启动", zap.InfoLevel)

	// ✅ 在服务启动时启动 Gin HTTP 服务
	go startGinServer()

	logBoth(elog, "服务已启动，监听 HTTP 请求", zap.InfoLevel)

	status <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
	logBoth(elog, "服务已启动", zap.InfoLevel)

loop:
	for {
		select {
		case r := <-req:
			switch r.Cmd {
			case svc.Stop, svc.Shutdown:
				logBoth(elog, "收到停止信号，准备退出", zap.InfoLevel)
				status <- svc.Status{State: svc.StopPending}
				break loop
			default:
				logBoth(elog, "收到未知命令", zap.WarnLevel)
			}
		}
	}

	logBoth(elog, "服务已停止", zap.InfoLevel)
	return false, 0
}

// 初始化 Zap + Lumberjack 日志系统
func initZapLogger() {
	logDir := "logs"
	_ = os.MkdirAll(logDir, 0755)
	logPath := filepath.Join(logDir, "service.log")

	writer := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logPath,
		MaxSize:    10, // 每个日志文件最大10MB
		MaxAge:     30, // 最多保存30天
		MaxBackups: 10, // 最多10个历史文件
		Compress:   true,
	})

	cfg := zap.NewProductionEncoderConfig()
	cfg.TimeKey = "time"
	cfg.EncodeTime = zapcore.ISO8601TimeEncoder
	cfg.EncodeLevel = zapcore.CapitalLevelEncoder

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(cfg),
		writer,
		zap.InfoLevel,
	)

	logger := zap.New(core, zap.AddCaller())
	zapLogger = logger.Sugar()
}

// 同时输出到 Zap 和 Windows 事件日志
func logBoth(elog *eventlog.Log, message string, level zapcore.Level) {
	switch level {
	case zap.InfoLevel:
		zapLogger.Infow(message)
		_ = elog.Info(1, message)
	case zap.WarnLevel:
		zapLogger.Warnw(message)
		_ = elog.Warning(1, message)
	case zap.ErrorLevel:
		zapLogger.Errorw(message)
		_ = elog.Error(1, message)
	default:
		zapLogger.Infow(message)
		_ = elog.Info(1, message)
	}
}

// 启动 Gin HTTP 接口
func startGinServer() {
	r := gin.Default()

	// 中间件：统计网络请求次数
	r.Use(func(c *gin.Context) {
		atomic.AddUint64(&networkRequests, 1)
		c.Next()
	})

	// 监控接口
	r.GET("/stats", func(c *gin.Context) {
		var memStats runtime.MemStats
		runtime.ReadMemStats(&memStats)

		uptime := time.Since(startTime)
		days := uptime / (24 * time.Hour)
		uptime %= 24 * time.Hour
		hours := uptime / time.Hour
		uptime %= time.Hour
		minutes := uptime / time.Minute
		uptime %= time.Minute
		seconds := uptime / time.Second

		c.JSON(http.StatusOK, gin.H{
			"uptime":           fmt.Sprintf("%dD %dH %dM %dS", days, hours, minutes, seconds),
			"memory_alloc_mb":  fmt.Sprintf("%.2f MB", float64(memStats.Alloc)/1024/1024),      // 当前内存分配 (MB)
			"memory_total_mb":  fmt.Sprintf("%.2f MB", float64(memStats.TotalAlloc)/1024/1024), // 总内存分配 (MB)
			"memory_sys_mb":    fmt.Sprintf("%.2f MB", float64(memStats.Sys)/1024/1024),        // 系统内存占用 (MB)
			"goroutines_count": runtime.NumGoroutine(),                                         // 当前 Goroutine 数量
			"network_requests": atomic.LoadUint64(&networkRequests),                            // 网络请求次数
		})
	})

	// 示例接口
	r.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	r.POST("/echo", func(c *gin.Context) {
		// 读取原始请求体
		rawData, err := c.GetRawData()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "无法读取请求体", "details": err.Error()})
			return
		}
		zapLogger.Infow(string(rawData))
		// 设置响应头为 JSON 并直接返回原始数据
		c.Data(http.StatusOK, "application/json", rawData)
	})

	// 从环境变量获取端口，默认为 8080
	port := os.Getenv("GIN_PORT")
	if port == "" {
		port = "8080"
	}

	zapLogger.Infof("Gin HTTP 服务已启动，监听端口: %s", port)
	if err := r.Run("0.0.0.0:" + port); err != nil {
		zapLogger.Fatalf("Gin HTTP 服务启动失败: %v", err)
	}
}

// 路径必须是绝对路径或服务启动目录下的相对路径，而且在运行为 Windows 服务时，工作目录 通常是 C:\Windows\System32，这会导致资源加载失败。
// 推荐你在代码中使用 os.Executable() 来获取当前可执行文件目录，以确保路径正确：
// staticDir := filepath.Join(getExecutableDir(), "static")
// templateDir := filepath.Join(getExecutableDir(), "templates")

// r.Static("/static", staticDir)
// r.LoadHTMLGlob(filepath.Join(templateDir, "*"))

func getExecutableDir() string {
	exePath, err := os.Executable()
	if err != nil {
		panic(err)
	}
	return filepath.Dir(exePath)
}
